package com.xd.core.openapi.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xd.common.config.RabbitConfig;
import com.xd.common.constant.SysConstant;
import com.xd.common.exception.XkdException;
import com.xd.common.util.AESUtil;
import com.xd.common.util.OrderNumUtil;
import com.xd.common.util.XkdUtil;
import com.xd.core.api.dto.*;
import com.xd.core.api.entity.CardProtocolInfo;
import com.xd.core.api.entity.RepaymentOrderInfo;
import com.xd.core.api.mapper.CardProtocolInfoMapper;
import com.xd.core.api.mapper.RepaymentOrderInfoMapper;
import com.xd.core.api.service.DLApiService;
import com.xd.core.business.entity.*;
import com.xd.core.business.mapper.*;
import com.xd.core.company.entity.Company;
import com.xd.core.company.mapper.CompanyMapper;
import com.xd.core.debt.dto.EarlyRepaymentDto;
import com.xd.core.debt.entity.Debt;
import com.xd.core.debt.mapper.DebtMapper;
import com.xd.core.debt.service.IDebtService;
import com.xd.core.debt.vo.EarlyRepayVo;
import com.xd.core.finance.dto.KeepBookingDto;
import com.xd.core.note.IAsyncService;
import com.xd.core.note.eventEntity.DebtEvent;
import com.xd.core.openapi.dto.*;
import com.xd.core.openapi.service.OpenapiService;
import com.xd.core.product.entity.Product;
import com.xd.core.product.mapper.ProductCompanyMapper;
import com.xd.core.product.mapper.ProductMapper;
import com.xd.core.system.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

//开放接口
@Service
@Slf4j
public class OpenapiServiceImpl implements OpenapiService {
    @Autowired
    private DLApiService dlApiService;
    @Autowired
    private CreditMapper creditMapper;
    @Autowired
    private RepaymentOrderInfoMapper repaymentOrderInfoMapper;
    @Autowired
    private RepaymentScheduleMapper repaymentScheduleMapper;
    @Autowired
    private DebtMapper debtMapper;
    private static final String REPAY_LOCK_KEY = "lock_repay";
    private static final String DEBT_LOCK_KEY = "lock_debt";
    private static final String CONTRACT_LOCK_KEY = "lock_contract";
    private static final int delay_time = 1000 * 60 * 60 * 4;
    @Autowired
    private IncomingInfoMapper incomingInfoMapper;
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private CompanyMapper companyMapper;
    @Autowired
    private OutInfoMapper outInfoMapper;
    @Autowired
    private ContractMapper contractMapper;
    @Autowired
    private ProductCompanyMapper productCompanyMapper;
    @Autowired
    private IDebtService debtService;
    @Autowired
    private FileService fileService;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    @Autowired
    private IAsyncService iAsyncService;
    @Autowired
    private CardProtocolInfoMapper cardProtocolInfoMapper;
    @Value("${aes.key}")
    private String key;
    private final String USED = "资金周转";

    public static void main(String[] args) {
        String decrypt = AESUtil.decrypt("TyACyRC4PR7Day+Gx3rNQQ==", "bd482fcff91d47cc");
        System.out.println(decrypt);
    }
    @Override
    public String readySign(OpenReadySignDto openReadySignDto) throws Exception {
        XkdUtil.checkNonNullProperties(openReadySignDto, "incomingId");
        ConfirmSignDto confirmSignDto = creditMapper.getUserAccount(openReadySignDto.getIncomingId());
        return dlApiService.bindBankCard(AESUtil.decrypt(confirmSignDto.getRepayPhone(),key), confirmSignDto.getRepayAccount(), confirmSignDto.getRepayName(), confirmSignDto.getRepayIdCard());
    }

    @Override
    public void confirmSign(OpenConfirmSignDto openConfirmSignDto) throws Exception {
        XkdUtil.checkNonNullProperties(openConfirmSignDto, "incomingId", "smsCode", "uniqueCode");
        if (openConfirmSignDto.getIncomingId() == null)
            throw new Exception("incomingId不能为空");
        ConfirmSignDto confirmSignDto = creditMapper.getUserAccount(openConfirmSignDto.getIncomingId());
        dlApiService.affirmBindCard(confirmSignDto.getCreditId(), openConfirmSignDto.getSmsCode(), openConfirmSignDto.getUniqueCode(), confirmSignDto.getRepayAccount(), confirmSignDto.getRepayName(), confirmSignDto.getRepayIdCard(), AESUtil.encrypt(confirmSignDto.getRepayPhone(), key),confirmSignDto.getRepayBank());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Integer fillInformation(OpenIncomingInfoDto openIncomingInfoDto) {
        Company company = companyMapper.selectById(openIncomingInfoDto.getCompanyId());
        if (SysConstant.INCOMING_STATUS_DISABLE.equals(company.getIncomingStatus())) {
            throw new XkdException("公司未开启进件开关");
        }
        //验证必填信息
        XkdUtil.checkNonNullProperties(openIncomingInfoDto, "loanAmount", "accountName", "receivingAccount",
                "bank", "repayAccount", "repayBank", "repayName", "name", "idCard", "phone", "productId", "term", "repayDay",
                "loanMethod", "repayFrequency", "rate", "address", "email");
        //限制还款频率为天，周，月(如果为月，还款日只能为1-30)
        String repayFrequency = openIncomingInfoDto.getRepayFrequency();
        int repayDay;
        try{
             repayDay = Integer.parseInt(openIncomingInfoDto.getRepayDay());
        }catch(Exception e){
            throw new XkdException("还款日格式错误");
        }
        String loanMethod = openIncomingInfoDto.getLoanMethod();
        //验证还款频率是否为有效类型
        if (!SysConstant.REPAY_FREQUENCY_DAY.equals(repayFrequency) 
                && !SysConstant.REPAY_FREQUENCY_MONTH.equals(repayFrequency) 
                && !SysConstant.REPAY_FREQUENCY_WEEK.equals(repayFrequency)) {
            throw new XkdException("还款频率只能为天、周、月");
        }
        //还款频率为月，还款日只能为1-30
        if (SysConstant.REPAY_FREQUENCY_MONTH.equals(repayFrequency)) {
            if ( repayDay < 1 || repayDay > 30) {
                throw new XkdException("还款频率为月时，还款日只能为1-30");
            }
        }
        //还款频率为周，还款日只能填1-7
        if (SysConstant.REPAY_FREQUENCY_WEEK.equals(repayFrequency)) {
            if (repayDay < 1 || repayDay > 7) {
                throw new XkdException("还款频率为周时，还款日只能为1-7");
            }
        }
        //还款频率为日，把repayDay赋值“/”,且还款方式只能选择一次性还本付清
        if (SysConstant.REPAY_FREQUENCY_DAY.equals(repayFrequency)) {
            openIncomingInfoDto.setRepayDay("/");
            if (!SysConstant.REPAY_TYPE_THREE.equals(loanMethod)) {
                throw new XkdException("还款频率为日时，还款方式只能选择一次性还本付清");
            }
        }
        //还款期数必须大于2
        if (openIncomingInfoDto.getTerm() < 2) {
            throw new XkdException("还款期数必须大于1");
        }
        //如果还款方式为一次性还本付清，还款频率只能选日
        if (SysConstant.REPAY_TYPE_THREE.equals(loanMethod)) {
            if (!SysConstant.REPAY_FREQUENCY_DAY.equals(repayFrequency)) {
                throw new XkdException("还款方式为一次性还本付清时，还款频率只能选择日");
            }
        }

        List<Integer> productIds = productCompanyMapper.selectProductIds(openIncomingInfoDto.getCompanyId());
        if (!productIds.contains(openIncomingInfoDto.getProductId())) {
            throw new XkdException("该公司未配置该产品");
        }
        Product product = productMapper.selectById(openIncomingInfoDto.getProductId());
        //校验授信金额，利率，贷款期限，还款方式,剩余授信金额
        BigDecimal rate = openIncomingInfoDto.getRate();
        String repayType = openIncomingInfoDto.getLoanMethod();
        if (!product.getRepayment().contains(repayType)) {
            throw new XkdException("贷款方式不符合产品贷款方式");
        }
        if (product.getRateMin().compareTo(rate) > 0 || product.getRateMax().compareTo(rate) < 0) {
            throw new XkdException("贷款利率超出产品贷款利率");
        }
        Integer term = openIncomingInfoDto.getTerm();
        if (SysConstant.REPAY_FREQUENCY_DAY.equals(repayFrequency)) {
            if (product.getLoanTermMinDay().compareTo(term) > 0 || product.getLoanTermMaxDay().compareTo(term) < 0) {
                throw new XkdException("贷款期限超出产品贷款期限");
            }
        } else if (SysConstant.REPAY_FREQUENCY_WEEK.equals(repayFrequency)) {
            if (product.getLoanTermMinMonth().compareTo(term) > 0 || product.getLoanTermMaxMonth().compareTo(term) < 0) {
                throw new XkdException("贷款期限超出产品贷款期限");
            }
        } else {
            if (product.getLoanTermMinMonth().compareTo(term) > 0 || product.getLoanTermMaxMonth().compareTo(term) < 0) {
                throw new XkdException("贷款期限超出产品贷款期限");
            }
        }
        BigDecimal outLoan = openIncomingInfoDto.getLoanAmount();
        if (outLoan.compareTo(product.getMaxAmount()) > 0 || outLoan.compareTo(product.getMinAmount()) < 0) {
            throw new XkdException("贷款金额超出产品金额范围");
        }

        //进件表插入数据
        IncomingInfo info = BeanUtil.copyProperties(openIncomingInfoDto, IncomingInfo.class);
        info.setIncomingType("2");
        info.setStatus(SysConstant.BUSINESS_STATUS_FIVE);
        String phone = info.getPhone();
        info.setPhone(AESUtil.encrypt(phone, key));
        incomingInfoMapper.insert(info);
        //授信表插入数据
        Credit credit = BeanUtil.copyProperties(openIncomingInfoDto, Credit.class);
        Integer incomingId = info.getIncomingId();
        credit.setIncomingId(incomingId);
        CardProtocolInfo cardProtocolInfo = cardProtocolInfoMapper.selectOne
                (new LambdaQueryWrapper<CardProtocolInfo>().eq(CardProtocolInfo::getRepayAccount, openIncomingInfoDto.getReceivingAccount()));
        if (ObjectUtil.isNull(cardProtocolInfo)) {
            credit.setCardStatus("0");
        } else {
            credit.setCardStatus("1");
        }
        creditMapper.insert(credit);
        return incomingId;
    }

    @Override
    public EarlyRepayVo earlyRepayment(OpenEarlyRepaymentDto check) {
        Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getIncomingId, check.getIncomingId()));
        if (ObjectUtil.isEmpty(debt)){
            throw new XkdException("该订单不存在");
        }
        EarlyRepaymentDto earlyRepaymentDto = new EarlyRepaymentDto();
        earlyRepaymentDto.setId(debt.getId());
        earlyRepaymentDto.setReduceAmount(check.getReduceAmount());
        earlyRepaymentDto.setStatus(String.valueOf(debt.getStatus()));
        earlyRepaymentDto.setType("1");//只要不是保单贷就行
        return debtService.earlyRepayment(earlyRepaymentDto,"2","1");
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void bookkeeping(OpenapiKeepBookingDto openkeepBookingDto) {
        KeepBookingDto keepBookingDto = BeanUtil.copyProperties(openkeepBookingDto, KeepBookingDto.class);
        Integer incomingId = keepBookingDto.getIncomingId();
        Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getIncomingId,incomingId));
        if (SysConstant.ORDER_STATUS_TWO.equals(debt.getStatus())){
            throw new XkdException("账单已还清，请勿重复操作");
        }
        keepBookingDto.setDebtId(debt.getId());
        Integer remainingRepaymentPeriods = debt.getRemainingRepaymentPeriods();
        keepBookingDto.setPeriods(remainingRepaymentPeriods);
        if (!"1" .equals(debt.getIsWithhold()) && !"1" .equals(debt.getIsOfflineRepay())) {
            throw new XkdException("用户不满足还款记账的条件");
        }
        if ("0".equals(debt.getIsEarly())){
            Integer id = keepBookingDto.getId();
            RepaymentSchedule repaymentSchedule = repaymentScheduleMapper.selectOne(new LambdaQueryWrapper<RepaymentSchedule>()
                            .eq(RepaymentSchedule::getId,id)
                            .eq(RepaymentSchedule::getIncomingId, incomingId));
            if (ObjectUtil.isEmpty(repaymentSchedule)){
                throw new XkdException("参数有误");
            }
            //验证上一期账单是否还清，必须按顺序还
            Integer currentPeriod = repaymentSchedule.getInstallmentNumber();
            if (currentPeriod > 1) {
                // 查找上一期账单
                RepaymentSchedule prevSchedule = repaymentScheduleMapper.selectOne(new LambdaQueryWrapper<RepaymentSchedule>()
                                .eq(RepaymentSchedule::getIncomingId, incomingId)
                                .eq(RepaymentSchedule::getInstallmentNumber, currentPeriod - 1));
                if (ObjectUtil.isNotEmpty(prevSchedule) && !SysConstant.REPAYMENT_STATUS_TOW.equals(prevSchedule.getStatus())) {
                    throw new XkdException("上一期账单未还清，请按顺序还款");
                }
            }

            if (SysConstant.REPAYMENT_STATUS_TOW.equals(repaymentSchedule.getStatus())){
                throw new XkdException("账单已还清，请勿重复操作");
            }
            repaymentScheduleMapper.updateAmount(keepBookingDto);
            keepBookingDto.setInterest(repaymentSchedule.getInterest());
            keepBookingDto.setPrincipal(repaymentSchedule.getPrincipal());
            debtMapper.updateAmount1(keepBookingDto);
            //生产贷款结清证明  异步调用
            Integer status = debt.getStatus();
            if (status == 2) {
                eventPublisher.publishEvent(new DebtEvent(debt));
            }
        } else //提前还款
        {
            outInfoMapper.updateClearDate(debt.getOutId());
            debtMapper.updateAmount(keepBookingDto);
            repaymentScheduleMapper.updateStatusByOutId(debt.getOutId());
            //生产贷款结清证明  异步调用
            eventPublisher.publishEvent(new DebtEvent(debt));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doContract(OpenapiContractDto openapiContractDto)  {
        Integer incomingId = openapiContractDto.getIncomingId();
        String controversy = openapiContractDto.getControversy();
        if (!(SysConstant.CONTROVERSY_TOW.equals(controversy) || SysConstant.CONTROVERSY_THREE.equals(controversy))) {
            throw new XkdException("争议方式选择错误，请重试");
        }
        String lockKey = CONTRACT_LOCK_KEY+incomingId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            log.info("尝试获取锁");
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                log.info("成功获取锁");
                OutInfo outInfo = outInfoMapper.selectOne(new LambdaQueryWrapper<OutInfo>().eq(OutInfo::getIncomingId, incomingId));
                Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, incomingId));

                if (ObjectUtil.isNotEmpty(outInfo) && !SysConstant.BUSINESS_STATUS_SIX.equals(outInfo.getStatus())) {
                    throw new XkdException("请勿重复签约");
                }
                if (ObjectUtil.isNotEmpty(outInfo)){
                    //先删除
                    List<String> removeContracts = contractMapper.selectRemoveContracts(outInfo.getOutId());
                    LambdaQueryWrapper<Contract> lqw = new LambdaQueryWrapper<>();
                    ArrayList<String> list = new ArrayList<>();
                    list.add(SysConstant.CONTRACT_TYPE_TOW);
                    list.add(SysConstant.CONTRACT_TYPE_THREE);
                    list.add(SysConstant.CONTRACT_TYPE_FIVE);
                    list.add(SysConstant.CONTRACT_TYPE_SIX);
                    list.add(SysConstant.CONTRACT_TYPE_SEVEN);
                    lqw.eq(Contract::getOutId, outInfo.getOutId()).in(Contract::getType, list);
                    contractMapper.delete(lqw);
                    fileService.filesRemove(removeContracts);
                    outInfo.setControversy(openapiContractDto.getControversy());
                    outInfo.setCityRemark(openapiContractDto.getCityRemark());
                    outInfoMapper.updateById(outInfo);
                }else{
                    //生成放款表,修改状态待签约
                    outInfo = new OutInfo();
                    BeanUtil.copyProperties(credit, outInfo);
                    outInfo.setStatus(SysConstant.BUSINESS_STATUS_SIX);
                    outInfo.setPayPass(SysConstant.CHANNEL_TYPE_TWO);
                    outInfo.setOutLoan(credit.getLoanAmount());
                    outInfo.setControversy(openapiContractDto.getControversy());
                    outInfo.setCityRemark(openapiContractDto.getCityRemark());
                    outInfo.setUsed(USED);
                    outInfoMapper.insert(outInfo);
                }
                SignContractDto signContractDto = new SignContractDto();
                signContractDto.setOutId(outInfo.getOutId());
                signContractDto.setIncomingId(incomingId);
                signContractDto.setUsed(USED);
                signContractDto.setLoanAmount(outInfo.getOutLoan());
                signContractDto.setRepayFrequency(outInfo.getRepayFrequency());
                signContractDto.setTerm(outInfo.getTerm());
                signContractDto.setRate(outInfo.getRate());
                signContractDto.setLoanMethod(outInfo.getLoanMethod());
                signContractDto.setControversy(controversy);
                signContractDto.setCityRemark(openapiContractDto.getCityRemark());
                signContractDto.setPayPass(SysConstant.CHANNEL_TYPE_TWO);
                signContractDto.setCreditId(credit.getCreditId());
                signContractDto.setCity(openapiContractDto.getCity());
                TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {//事务提交完成后执行
                        iAsyncService.signContract(signContractDto);
                    }
                });
            } else {
                throw new XkdException("服务器繁忙请稍后再试");
            }
        } catch (Exception e) {
            log.error("签署合同失败，请求参数: {}, 锁键: {}, 异常信息: {}", openapiContractDto, lockKey, e.getMessage(), e);
            throw new XkdException(e.getMessage());
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("签署合同释放锁，锁键: {}", lockKey);
            }
        }


    }

    @Override
    public void singlePay(OpenSinglePayDto check, Integer companyId) throws Exception {
        SinglePayDto singlePayDto = creditMapper.getSingleDto(check.getIncomingId());
        if (StringUtils.isBlank(check.getType()))
            throw new XkdException("type不能为空");
        if (!check.getType().equals("1") && !check.getType().equals("2"))
            throw new XkdException("type类型错误");
        if (check.getType().equals("1")) {
            //校验账单是否属于该企业
            Integer repayCount = repaymentScheduleMapper.checkRepay(check.getIncomingId(), check.getOrderId());
            if (repayCount == 0)
                throw new RuntimeException("账单不存在");
            singlePayDto.setType(SysConstant.DEDUCT_MONEY_TYPE_TOW);
        } else {
            singlePayDto.setType(SysConstant.DEDUCT_MONEY_TYPE_FIVE);
        }

        singlePayDto.setIncomingId(check.getIncomingId());
        singlePayDto.setOrderId(check.getOrderId());
        singlePayDto.setCompanyId(companyId);
        String type = singlePayDto.getType();
        String lockKey = null;
        if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW)) {
            lockKey = REPAY_LOCK_KEY + singlePayDto.getOrderId();
        } else {
            lockKey = DEBT_LOCK_KEY + singlePayDto.getDebtId();
        }
        RLock lock = redissonClient.getLock(lockKey);

        try {
            log.info("尝试获取锁");
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                log.info("成功获取锁");
                Credit credit = creditMapper.selectOne(new LambdaQueryWrapper<Credit>().eq(Credit::getIncomingId, singlePayDto.getIncomingId()));
                Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getId, singlePayDto.getDebtId()));
                Integer outId = debt.getOutId();
//                if (debt.getStatus().equals(SysConstant.ORDER_STATUS_FOUR) || debt.getStatus().equals(SysConstant.ORDER_STATUS_FIVE))
//                    throw new XkdException("该订单已退保");
                if (debt.getStatus().equals(SysConstant.ORDER_STATUS_TWO))
                    throw new XkdException("该债权已结清");
                if (debt.getIsWithhold().equals("1"))
                    throw new XkdException("该债权转让后未选择代收付");
                RepaymentOrderInfo repaymentOrderInfo = new RepaymentOrderInfo();
                if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW)) {
                    if(debt.getIsEarly().equals("1"))
                        throw new XkdException("已申请提前还款，无法代扣本期");
                    //查询是否有申请中的还款
                    Integer i = repaymentOrderInfoMapper.getRepayOrderStatus(singlePayDto.getOrderId());
                    if (i > 0)
                        throw new XkdException("有申请中的还款或已完成还款");
                    //判断以前是否有未还或者逾期的账单
                    Integer j = repaymentScheduleMapper.getIsUntreated(outId, singlePayDto.getOrderId());
                    if (j > 0)
                        throw new XkdException("前面有未处理的账单");
                    RepaymentSchedule repaymentSchedule = repaymentScheduleMapper.selectById(singlePayDto.getOrderId());
                    if (repaymentSchedule.getStatus().equals(SysConstant.REPAYMENT_STATUS_TOW))
                        throw new XkdException("本期已还");
                    if (repaymentSchedule.getRepayTimes() >= 2)
                        throw new XkdException("手动代扣每日限制2次");
                    repaymentOrderInfo.setOrderId(singlePayDto.getOrderId());
                    //支付金额要加上逾期利息 罚息
                    Integer amount1 = repaymentSchedule.getAmount().multiply(new BigDecimal(100)).intValue();
                    Integer amount2 = debt.getOverdueInterest().add(debt.getDefaultInterestTodo()).multiply(new BigDecimal(100)).intValue();
                    int amount = amount1 + amount2;
                    repaymentOrderInfo.setAmount(amount);
                    singlePayDto.setAmount(String.valueOf(amount));
                } else {
                    //查询债权表是否有申请中的代扣
                    Integer i = repaymentOrderInfoMapper.getDebtOrderStatus(singlePayDto.getDebtId());
                    if (i > 0)
                        throw new XkdException("有申请中的还款或已完成还款");
                    if (debt.getStatus().equals(2))
                        throw new XkdException("已结清");
                    if (debt.getWithholdingTimes() >= 2)
                        throw new XkdException("手动代扣每日限制2次");
                    if (!debt.getIsEarly().equals("1"))
                        throw new XkdException("未申请提前还款");
                    int amount = debt.getEarlyAmount().multiply(new BigDecimal(100)).intValue();
                    repaymentOrderInfo.setAmount(amount);
                    singlePayDto.setAmount(String.valueOf(amount));
                }
                String repayOrder = OrderNumUtil.getRepayOrder();
                repaymentOrderInfo.setDebtId(singlePayDto.getDebtId());
                repaymentOrderInfo.setOrderNo(repayOrder);
                repaymentOrderInfo.setType(singlePayDto.getType());
                repaymentOrderInfo.setCompanyId(singlePayDto.getCompanyId());
                repaymentOrderInfo.setRepayName(credit.getRepayName());
                repaymentOrderInfo.setRepayAccount(credit.getRepayAccount());
                repaymentOrderInfo.setRepayBank(credit.getRepayBank());
                repaymentOrderInfo.setIncomingId(singlePayDto.getIncomingId());
                repaymentOrderInfo.setOutId(outId);
                //插入订单
                repaymentOrderInfoMapper.insert(repaymentOrderInfo);
                singlePayDto.setOrderNo(repayOrder);
                dlApiService.singlePay(singlePayDto, repaymentOrderInfo);
                if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_TOW))
                    repaymentScheduleMapper.updateRepayTimes(singlePayDto.getOrderId());//增加代扣次数
            } else {
                throw new XkdException("服务器繁忙请稍后再试");
            }
        } catch (Exception e) {
            log.error("手动代扣失败，请求参数: {}, 锁键: {}, 异常信息: {}", singlePayDto, lockKey, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("手动代扣任务释放锁，锁键: {}", lockKey);
            }
        }
    }

    @Override
    public void offlinePay(OPenOfflinePayDto check, Integer appid) throws Exception {

        OfflinePayDto offlinePayDto = creditMapper.getOfflinePayDto(check.getIncomingId());
        offlinePayDto.setCompanyId(appid);
        offlinePayDto.setRepayId(check.getOrderId());
        if (StringUtils.isBlank(check.getType()))
            throw new XkdException("type不能为空");
        if (!check.getType().equals("1") && !check.getType().equals("2"))
            throw new XkdException("type类型错误");
        if (check.getType().equals("1")) {
            //校验账单是否属于该企业
            Integer repayCount = repaymentScheduleMapper.checkRepay(check.getIncomingId(), check.getOrderId());
            if (repayCount == 0)
                throw new RuntimeException("账单不存在");
            offlinePayDto.setType(SysConstant.DEDUCT_MONEY_TYPE_THREE);
        } else {
            offlinePayDto.setType(SysConstant.DEDUCT_MONEY_TYPE_SIX);
        }
        offlinePayDto.setIncomingId(check.getIncomingId());
        offlinePayDto.setPayerUserName(check.getPayerUserName());
        offlinePayDto.setPayerAcctCode(check.getPayerAcctCode());
        offlinePayDto.setPayerBank(check.getPayerBank());

        String type = offlinePayDto.getType();
        String lockKey = null;
        if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE)) {
            lockKey = REPAY_LOCK_KEY + offlinePayDto.getRepayId();
        } else {
            lockKey = DEBT_LOCK_KEY + offlinePayDto.getDebtId();
        }
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                Debt debt = debtMapper.selectOne(new LambdaQueryWrapper<Debt>().eq(Debt::getId, offlinePayDto.getDebtId()));
                Integer outId = debt.getOutId();
//                if (debt.getStatus().equals(SysConstant.ORDER_STATUS_FOUR) || debt.getStatus().equals(SysConstant.ORDER_STATUS_FIVE))
//                    throw new XkdException("该订单已退保");
                if (debt.getStatus().equals(SysConstant.ORDER_STATUS_TWO))
                    throw new XkdException("该债权已结清");
                if (debt.getIsWithhold().equals("1"))
                    throw new XkdException("该债权转让后未选择代收付");
                Integer i = null;
                if (type.equals(SysConstant.DEDUCT_MONEY_TYPE_THREE)) {
                    if(debt.getIsEarly().equals("1"))
                        throw new XkdException("已申请提前还款，无法代扣本期");
                    //查询是否有申请中的代扣
                    i = repaymentOrderInfoMapper.getRepayOrderStatus(offlinePayDto.getRepayId());
                    //判断以前是否有未还或者逾期的账单
                    Integer j = repaymentScheduleMapper.getIsUntreated(outId, offlinePayDto.getRepayId());
                    if (j > 0)
                        throw new XkdException("前面有未处理的账单");
                } else {
                    if (!debt.getIsEarly().equals("1"))
                        throw new XkdException("未申请提前还款");
                    //查询债权表是否有申请中的代扣
                    i = repaymentOrderInfoMapper.getDebtOrderStatus(offlinePayDto.getDebtId());
                }
                if (i > 0)
                    throw new XkdException("有申请中的还款或已完成还款");
                String s = dlApiService.offlinePay(offlinePayDto);
                //发送延迟消息 8小时
                DelayOfflinePayDto delayOfflinePayDto = new DelayOfflinePayDto();
                delayOfflinePayDto.setRepaymentOrderNo(s);
                delayOfflinePayDto.setOrderId(offlinePayDto.getRepayId());
                delayOfflinePayDto.setDebtId(offlinePayDto.getDebtId());
                delayOfflinePayDto.setType(offlinePayDto.getType());
                sendDelayOfflinePayMessage(delayOfflinePayDto, delay_time);
            } else {
                throw new XkdException("服务器繁忙，请勿重复提交");
            }
        } catch (Exception e) {
            log.error("线下转账失败，请求参数: {}, 锁键: {}, 异常信息: {}", offlinePayDto, lockKey, e.getMessage(), e);
            throw e;
        } finally {
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
                log.info("手动代扣任务释放锁，锁键: {}", lockKey);
            }
        }
    }

    private void sendDelayOfflinePayMessage(DelayOfflinePayDto msg, int delayMillis) {
        log.info("发送线下还款订单延迟消息");
        rabbitTemplate.convertAndSend(
                RabbitConfig.DELAY_EXCHANGE,
                RabbitConfig.DELAY_ROUTING_KEY,
                msg,
                message -> {
                    message.getMessageProperties().setDelay(delayMillis);
                    return message;
                });
    }
}
